/*
 * IXmlNode.cpp
 *
 *  Created on: 08.06.2012
 *      Author: caffeine
 */

#include "CXmlNode.h"
#include <lampe/xml/IXMLSaxParser.h>
#include <lampe/StringUtils.h>
#include "CAttribute.h"
//#include "exception/Null.h"
//#include <debug_new/debug_new.h>

namespace lampe{
namespace xml{
u32 node_count=0;

String convertStringToXML(String str){
	return str;
}

String convertStringToCDATAXML(String str){
	return str;
}

String convertCDATAtoString(String str){
	return str;
}

String convertXMLTOString(String str){
	return str;
}

void CXmlNode::setTag(void*arg){

	tagObject=arg;
	return;
}

void*CXmlNode::getTag(){

	return (tagObject);
}

IXmlNode*CXmlNode::findNodeNotNull(bool checkThis){

	if (checkThis && getTag())
		return (this);
	if (!getParent())
		return (0);
	return (getParent()->findNodeNotNull(true));
}

IXmlNode*CXmlNode::giveNext(IXmlNode*arg){

	s32 index=indexOf(arg);
	if (arg==this && getNodeCount()>0)
		return (getNode(0));
	if (index<0 && getParent())//если данный узел не найден в текущем И у текущего узла есть родитель
		return (getParent()->giveNext(this));//то переадресовать запрос выше
	if (index<0 && !getParent())//если данный узел не найден в текущем И у текущего узла нет родителя
		return (0);//то возвращяется, что ничего не найдено
	if (index==((s32)getNodeCount())-1 && getParent())//если данный узел является последним элементом текущего узла И у текущего узла есть родитель
		return (getParent()->giveNext(this));//то запрос переадрисуется выше
	if (index==((s32)getNodeCount())-1 && !getParent())//если данный узел является последним элементом текущего узла И у текущего узла НЕТ родителя
		return (0);//то возвращяем, что ничего не найдено
	return (getNode(index+1));
}

void CXmlNode::clearAttributes(){

	for (u32 i=0;i<attributes.size();i++)
		attributes[i]->drop();
	attributes.clear();
	return;
}

void CXmlNode::addAttributeObj(IAttribute*a){

	a->grab();
	attributes.push_back(a);
	return;
}

void CXmlNode::addAttribute(String name,String value){

	IAttribute*a=new CAttribute(name,value);
	addAttributeObj(a);
	a->drop();
	return;
}

u32 CXmlNode::getAttributeCount(){

	return (attributes.size());
}

IAttribute*CXmlNode::getAttribute(u32 index){

	if (index>=getAttributeCount())
		return (0);
	return (attributes[index]);
}

void CXmlNode::setName(String arg){

	name=arg;
	str::toLowerCase(name);
	updatePath();
	return;
}


String CXmlNode::getName(){
	return name;
}


/*
core::rect<s32> CXmlNode::getAttRect2I(String name){
	s32 index=indexOfAttribute(name);
	if (index<=-1 || index>=getAttributeCount())
		return core::rect<s32>(0,0,0,0);
	return attributes[index]->getRecti();
}

core::rect<f32> CXmlNode::getAttRect2F(String name){
	s32 index=indexOfAttribute(name);
	if (index<=-1 || index>=getAttributeCount())
		return core::rect<f32>(0,0,0,0);
	return attributes[index]->getRectf();
}
*/
Vector2di CXmlNode::getAttVec2I(String name,s32 X,s32 Y){
	s32 index=indexOfAttribute(name);
	if (index<=-1 || index>=(s32)getAttributeCount())
		return Vector2di(X,Y);
	return attributes[index]->getVector2di();
}

Vector2df CXmlNode::getAttVec2F(String name,f32 X,f32 Y){
	s32 index=indexOfAttribute(name);
	if (index<=-1 || index>=(s32)getAttributeCount())
		return Vector2df(X,Y);
	return attributes[index]->getVector2df();
}

Vector3di CXmlNode::getAttVec3I(String name,s32 X,s32 Y,s32 Z){
	s32 index=indexOfAttribute(name);
	if (index<=-1 || index>=(s32)getAttributeCount())
		return Vector3di(X,Y,Z);
	return attributes[index]->getVector3di();
}

Vector3df CXmlNode::getAttVec3F(String name,f32 X,f32 Y,f32 Z){
	s32 index=indexOfAttribute(name);
	if (index<=-1 || index>=(s32)getAttributeCount())
		return Vector3df(X,Y,Z);
	return attributes[index]->getVector3df();
}

SColor CXmlNode::getAttColor(String name,u32 A,u32 R,u32 G,u32 B){
	s32 index=indexOfAttribute(name);
	if (index<=-1 || index>=(s32)getAttributeCount())
		return SColor(A,R,G,B);
	return attributes[index]->getColor();
}

void CXmlNode::setAttS32(String name,s32 value){
	s32 index=indexOfAttribute(name);
	IAttribute*att;
	if (index>=0)
		att=attributes[index];
	else{
		att=xml::createAttribute(name);
		addAttributeObj(att);
		att->drop();
	}
	att->setS32(value);
}

void CXmlNode::setAttString(String name,String value){
	s32 index=indexOfAttribute(name);
	IAttribute*att;
	if (index>=0)
		att=attributes[index];
	else{
		att=xml::createAttribute(name);
		addAttributeObj(att);
		att->drop();
	}
	att->setString(value);
}

void CXmlNode::setAttBool(String name,bool value){
	s32 index=indexOfAttribute(name);
	IAttribute*att;
	if (index>=0)
		att=attributes[index];
	else{
		att=xml::createAttribute(name);
		addAttributeObj(att);
		att->drop();
	}
	att->setBool(value);
}

/*
void CXmlNode::(String name,core::rect<s32> value){
	s32 index=indexOfAttribute(name);
	IAttribute*att;
	if (index>=0)
		att=attributes[index];
	else{
		att=xml::createAttribute(name);
		addAttributeObj(att);
		att->drop();
	}
	att->setRectI(value);
}
void CXmlNode::setAttRect2F(String name,core::rect<f32> value){
	s32 index=indexOfAttribute(name);
	IAttribute*att;
	if (index>=0)
		att=attributes[index];
	else{
		att=xml::createAttribute(name);
		addAttributeObj(att);
		att->drop();
	}
	att->setRectF(value);
}
*/
void CXmlNode::setAttVec2I(String name,Vector2di value){
	s32 index=indexOfAttribute(name);
	IAttribute*att;
	if (index>=0)
		att=attributes[index];
	else{
		att=xml::createAttribute(name);
		addAttributeObj(att);
		att->drop();
	}
	att->setVector2di(value);
}
void CXmlNode::setAttVec2F(String name,Vector2df value){
	s32 index=indexOfAttribute(name);
	IAttribute*att;
	if (index>=0)
		att=attributes[index];
	else{
		att=xml::createAttribute(name);
		addAttributeObj(att);
		att->drop();
	}
	att->setVector2df(value);
}

void CXmlNode::setAttVec3I(String name,Vector3di value){
	s32 index=indexOfAttribute(name);
	IAttribute*att;
	if (index>=0)
		att=attributes[index];
	else{
		att=xml::createAttribute(name);
		addAttributeObj(att);
		att->drop();
	}
	att->setVector3di(value);
}

void CXmlNode::setAttVec3F(String name,Vector3df value){
	s32 index=indexOfAttribute(name);
	IAttribute*att;
	if (index>=0)
		att=attributes[index];
	else{
		att=xml::createAttribute(name);
		addAttributeObj(att);
		att->drop();
	}
	att->setVector3df(value);
}

void CXmlNode::setAttColor(String name,SColor value){
	s32 index=indexOfAttribute(name);
	IAttribute*att;
	if (index>=0)
		att=attributes[index];
	else{
		att=xml::createAttribute(name);
		addAttributeObj(att);
		att->drop();
	}
	att->setColor(value);
}
/*
void CXmlNode::parse(CXMLSaxParser*reader){
	IXmlNode*self=(IXmlNode*)reader->getTag();
	if (reader){
			if (reader->getNodeType()==CXMLSaxParser::ELEMENT_START || reader->getNodeType()==CXMLSaxParser::ELEMENT_END){
				setName(reader->getNodeName());
				//setBody("");
				for (u32 i=0;i<reader->getAttributeCount();i++)
					addAttribute(reader->getAttributeName(i),reader->getAttributeValue(i));
			}else{
				setName("");
				//setBody(reader->getNodeData());
			}
		}else{
			setName("");
			//setBody("");
		}
		setParent(Parent);
		if (reader){
			if (getName().size()<=0){
				///LOG("++++ %s",getName().c_str());
				return;
			}
			if (!reader->isEmptyElement())
				while (reader->read()){
					if (reader->getNodeType()==CXMLSaxParser::ELEMENT_END && String(reader->getNodeName())==getName()){
						//LOG("++++ %s",getName().c_str());
						return;
					}
					if (reader->getNodeType()==CXMLSaxParser::ELEMENT_START || reader->getNodeType()==CXMLSaxParser::ELEMENT_END || (reader->getNodeType()==CXMLSaxParser::ELEMENT_TEXT && String(reader->getNodeData()).trim().size()>0)){
						IXmlNode*d=new IXmlNode(reader,current_dir,this);
						d->drop();
					}
				}
		}
}
*/

CXmlNode::CXmlNode(){
	node_count++;
	current_dir="";
	parent=0;
	tagObject=0;
}

CXmlNode::CXmlNode(CXMLSaxList*reader,String cd,IXmlNode*Parent){
	node_count++;
	current_dir=cd;
	parent=0;
	tagObject=0;


	if (reader){
		reader->grab();
				if (reader->getNodeType()==IXMLSaxParser::ELEMENT_START || reader->getNodeType()==IXMLSaxParser::ELEMENT_END){
					setName(reader->getNodeName());
					body="";
					for (u32 i=0;i<reader->getAttributeCount();i++)
						addAttribute(reader->getAttributeName(i),reader->getAttributeValue(i));
				}else{
					setName("");
					cdata=reader->isCDATA();
					if (cdata)
						body=convertCDATAtoString(reader->getNodeData());
					else
						body=convertXMLTOString(reader->getNodeData());
				}
			}else{
				setName("");
				body=("");
			}
			setParent(Parent);
			if (reader){
				if (getName().size()<=0){
					///LOG("++++ %s",getName().c_str());
					reader->drop();
					return;
				}
				if (!reader->isEmptyElement())
					while (reader->read()){
						if (reader->getNodeType()==IXMLSaxParser::ELEMENT_END && String(reader->getNodeName())==getName()){
							//LOG("++++ %s",getName().c_str());
							reader->drop();
							return;
						}
						if (reader->getNodeType()==IXMLSaxParser::ELEMENT_START || reader->getNodeType()==IXMLSaxParser::ELEMENT_END || (reader->getNodeType()==IXMLSaxParser::ELEMENT_TEXT && String(str::trim(reader->getNodeData())).length()>0)){
							IXmlNode*d=new CXmlNode(reader,cd,this);
							d->drop();
						}
					}
				reader->drop();
			}
}
/*
CXmlNode::CXmlNode(CReader*reader,String cd,IXmlNode*Parent) {
	CXMLSaxList*list=new CXMLSaxList(reader);
	CXmlNode::CXmlNode(list,cd,Parent);
	list->drop();
}
*/

IXmlNode*CXmlNode::getParent(){

	return (parent);
}

void CXmlNode::updatePath(){
	//FIXME Вот тут возможно касяки с памятью.
	path=getName();
	IXmlNode*cur=getParent();
	while (cur){
		path=cur->getName()+"/"+path;
		cur=cur->getParent();
	}
}

void CXmlNode::setParent(IXmlNode*Parent){


	if (Parent){
		Parent->addNode(this);
	}

	if (getParent()){
		getParent()->removeNode(this);
	}

	parent=Parent;
	updatePath();
}

void CXmlNode::setParentEx(IXmlNode*Parent,u32 index){
	if (Parent){
		Parent->insertNode(this,index);
	}
	if (getParent()){
		getParent()->removeNode(this);
	}
	parent=Parent;
	updatePath();
}

void CXmlNode::addNode(IXmlNode*node){
	if (indexOf(node)>=0){
		return;
	}
	node->grab();
	nodes.push_back(node);
}

void CXmlNode::insertNode(IXmlNode*node,u32 index){
	if (indexOf(node)>=0){
		return;
	}
	node->grab();

	nodes.insert(nodes.begin()+index,node);
}

u32 CXmlNode::getNodeCount(){

	return (nodes.size());
}

void CXmlNode::removeNodeByIndex(u32 index){

	if (index>=getNodeCount()){
		return;
	}
	nodes[index]->drop();
	nodes.erase(nodes.begin()+index);
	return;
}

void CXmlNode::removeNode(IXmlNode*node){

	if (!node){
		//GEN_NULL_ERROR("");
		return;
	}
	s32 index=indexOf(node);
	if (index<0){
		return;
	}
	removeNodeByIndex(index);
	return;
}

s32 CXmlNode::indexOf(IXmlNode*node){

	for (u32 i=0;i<getNodeCount();i++)
		if (getNode(i)==node)
			return i;
	return (-1);
}

CXmlNode::~CXmlNode(){

	node_count--;
	//LOG("---- %s",getName().c_str());
	//setParent(0);

	clearAttributes();
	clearNodes();
	return;
}

String CXmlNode::getPath(){

	return (path);

}

bool CXmlNode::isPartOfPath(String path){

	for (u32 i=0;i<path.length();i++)
		path[i]=tolower(path[i]);

	if (path.size()>getPath().size())
		return false;
	s32 index=getPath().size()-path.size();
	return getPath().find(path.c_str(),index)==(u32)index;
}

IXmlNode*CXmlNode::getNode(u32 index){
	if (index>=getNodeCount())
		return 0;
	return nodes[index];
}

void CXmlNode::setNode(u32 index,IXmlNode*node){
	if (!node){
		//GEN_NULL_ERROR("Can't set node");
		exit(0);
	}
	if (index>=nodes.size())
		return;
	nodes[index]->drop();
	nodes[index]=node;
	node->grab();
}

void CXmlNode::clearNodes(){
	if (getNodeCount()<=0)
		return;
	s32 count=getNodeCount()-1;
	while (count>=0){
		getNode(count)->drop();
		count--;
	}
	nodes.clear();
}

String CXmlNode::getCurrentDirectory(){
	return current_dir;
}

s32 CXmlNode::indexOfAttribute(String Name){

	Name=str::toLowerCase(Name);
	for (u32 i=0;i<getAttributeCount();i++)
		if (getAttribute(i)->getName()==Name)
			return (i);
	return (-1);
}

void CXmlNode::getNodes(String Name,LAMPE_VECTOR_VENDOR<IXmlNode*>*res,bool R){
	Name=str::toLowerCase(Name);

	if (!res)
		return;
	for (u32 i=0;i<getNodeCount();i++){
		if (getNode(i)->getName()==Name){
			res->push_back(getNode(i));
		}
		if (R)
			getNode(i)->getNodes(Name,res,R);
	}
}

LAMPE_VECTOR_VENDOR<IXmlNode*>CXmlNode::getNodes(String Name,bool R){
	for (u32 i=0;i<Name.length();i++)
		Name[i]=tolower(Name[i]);

	LAMPE_VECTOR_VENDOR<IXmlNode*> res;
	getNodes(Name,&res,R);
	return (res);
}

String CXmlNode::getAttString(String name,String def){

	s32 index=this->indexOfAttribute(name);
	if (index<0)
		return (def);
	return (this->getAttribute(index)->getString());
}

bool CXmlNode::getAttBool(String name,bool def){

	s32 index=indexOfAttribute(name);
	if (index<0)
		return (def);
	return (getAttribute(index)->getBool());
}

f32 CXmlNode::getAttF32(String name,f32 def){

	s32 index=indexOfAttribute(name);
	if (index<0)
		return (def);
	return (this->getAttribute(index)->getF32());
}

u32 CXmlNode::getAttU32(String name,u32 def){

	s32 index=this->indexOfAttribute(name);
	if (index<0)
		return (def);
	return (this->getAttribute(index)->getU32());
}

s32 CXmlNode::getAttS32(String name,s32 def){

	s32 index=this->indexOfAttribute(name);
	if (index<0)
		return (def);
	return (this->getAttribute(index)->getS32());
}

void CXmlNode::dropChildTo(IXmlNode*node){
	if (!node)
		return;
	for (u32 i=0;i<getNodeCount();i++){
		getNode(i)->setParent(node);
	}
}

void CXmlNode::dropChildToEx(IXmlNode*node,u32 index){
	if (!node)
		return;
	for (u32 i=0;i<getNodeCount();i++){
		getNode(i)->setParentEx(node,index);
	}
}

void CXmlNode::removeAttribute(u32 index){

	if (index>=getAttributeCount())
		return;
	attributes[index]->drop();
	attributes.erase(attributes.begin()+index);
	return;
}

void CXmlNode::dropAttributesTo(IXmlNode*node){

	if (!node)
		return;
	for (u32 i=0;i<getAttributeCount();i++){
		node->addAttributeObj(getAttribute(i));
		getAttribute(i)->drop();
	}
	attributes.clear();
	return;
}

void CXmlNode::dropAttributesUp(){

	dropAttributesTo(getParent());
	return;
}

void CXmlNode::dropChildUp(){
	dropChildTo(getParent());
}



String CXmlNode::toXML(u32 level){

	String res="";
	if (getName().size()<=0){
		for (u32 i=0;i<level;i++)
			res+="\t";
		if (cdata){
			res+="<![CDATA[";
			res+=convertStringToCDATAXML(body);
			res+="]]>";
		}else
			res+=convertStringToXML(body);
		return (res);
	}

	for (u32 i=0;i<level;i++)
		res+="\t";
	res=res+"<"+getName();
	for (u32 i=0;i<getAttributeCount();i++)
		res=res+" "+getAttribute(i)->toXML();
	if (getNodeCount()<=0)
		res=res+"/>";
	else
		res=res+">";
	u32 l=level+1;
	for (u32 i=0;i<getNodeCount();i++)
		res=res+"\n"+getNode(i)->toXML(l);

	if (getNodeCount()>0){
			res=res+"\n";
		for (u32 i=0;i<level;i++)
			res+="\t";
		res=res+"</"+getName()+">";
	}
	return (res);
}

void CXmlNode::serialization(lampe::stream::IWriter*stream){
    stream->writeStringc(getName());
    stream->writeU32(getNodeCount());
    for (u32 i=0;i<getNodeCount();i++)
        getNode(i)->serialization(stream);
}

void CXmlNode::deserialization(lampe::stream::IReader*stream){
    setName(stream->readStringc());
    u32 count=stream->readU32();
    for (u32 i=0;i<count;i++){
        IXmlNode*node=createXmlNode();
        node->deserialization(stream);
    }

}


IXmlNode*createXmlNode(){
	return new CXmlNode();
}

}//namespace xml
}//namespace irr
